package main

import "fmt"

func main() {
	const s = "Go101.org" //长度为9
	// len(s) == 9
	// 1 << 9 == 512
	// 512 / 128 == 4

	/*
		内置函数 len 和 cap获取各种类型的实参并返回一个int类型结果. 实现会保证结果总是一个 int值.
			1.如果 s 是一个字符串常量, 那么 len(s)是一个常量.
			2.如果 s 类型是一个数组或到数组的指针, 且表达式 s 不包含 信道接收 或 (非常量的) 函数调用的话,
				那么表达式 len(s) 和 cap(s) 是常量;   这种情况下,s 是不求值的.
			3.否则的话, len 和 cap 的调用结果不是常量且 s 会被求值
	*/

	/*
		位移操作
		Go 语言规范中有这么一句:
		在位移表达式的右侧的操作数必须为整数类型, 或者可以被 uint类型的值所表示的无类型的常量. 如果一个非
		常量位移表达式的左侧的操作数是一个无类型常量, 那么它会先被隐式地转换为假如位移表达式被其左侧操作数
		单独替换后的类型.
		这里的关键在于常量位移表达式. 根据上文的分析, 1<<len(s) 是常量位移表达式, 而1<<len(s[:]) 不是
		规范上关于常量表达式中, 还有这么一句:
		如果常量 位移表达式 的左侧操作数是一个无类型常量, 那么其结果是一个整数常量: 否则就是和左侧操作数桶
	*/

	//a输出4
	//s为常量, 直接计算常量的长度9, 然后进行左移9位, 512, 然后除以128, 得出结果4
	var a byte = 1 << len(s) / 128

	//b输出0
	// len()函数, 如果其中参数类型为常量, 那么返回的长度数就是常量
	// 但这里s类型为变量, 那么会和左边的1进行联合运算, 并且由于1不知道是什么类型,
	// 所以会转为左边结果的byte类型, 由于byte类型长度为8位, 这里操作向左移动了9位, 直接8个位置上都是0
	// 所以剩下 0 / 128, 结果为0
	var b byte = 1 << len(s[:]) / 128
	fmt.Println(a)
	fmt.Println(b)
}
